<!--
 * @Author: Xujianchen
 * @Date: 2024-08-02 10:48:47
 * @LastEditors: xujianchen
 * @LastEditTime: 2024-08-02 16:51:46
 * @Description: 图片裁剪组件
-->
<template>
  <div class="cropper">
    <div class="cropper-img">
      <img
        ref="imgRef"
        :src="src"
      />
    </div>
    <div class="cropper-preview">
      <h4>{{ title }}</h4>
      <div
        ref="previewRef"
        class="cropper-preview-img"
      />
    </div>
  </div>
</template>

<script setup>
import { isFunc, isArray } from '@/utils/type'
import { cropperProps, dftOptions } from './props'

import Cropper from 'cropperjs'
import 'cropperjs/dist/cropper.css'

const props = defineProps(cropperProps)

const previewRef = ref(null)
const cropperRef = ref(null)
const imgRef = ref(null)
let scaleX = 1
let scaleY = 1

onMounted(() => {
  const config = props
  cropperRef.value = new Cropper(imgRef.value, {
    ...dftOptions,
    viewMode: config.viewMode,
    aspectRatio: config.aspectRatio,
    preview: previewRef.value,
  })
})

watch(
  () => props.aspectRatio,
  (newVal) => setAspectRatio(newVal),
)

function setAspectRatio(aspectRatio) {
  cropperRef.value.setAspectRatio(aspectRatio)
}

function getCropData(callback, type = 'image/jpeg') {
  if (!isFunc(callback)) {
    throw new Error('first parameter: callback must be a function')
  }
  callback(cropperRef.value.getCroppedCanvas().toDataURL(type, props.compress))
}

function getCropBlob(callback, type = 'image/jpeg') {
  if (!isFunc(callback)) {
    throw new Error('first parameter: callback must be a function')
  }
  cropperRef.value
    .getCroppedCanvas()
    .toBlob((blob) => callback(blob), type, props.compress)
}

function getCropFile(callback, fileName = 'fileName.jpg', type = 'image/jpeg') {
  if (!isFunc(callback)) {
    throw new Error('first parameter: callback must be a function')
  }
  cropperRef.value.getCroppedCanvas().toBlob(
    (blob) => {
      let file = new File([blob], fileName, { type: type })
      callback(file)
    },
    type,
    props.compress,
  )
}

function handleCropper(eventName, arg) {
  if (eventName === 'scaleX') {
    scaleX = arg = scaleX === -1 ? 1 : -1
  }

  if (eventName === 'scaleY') {
    scaleY = arg = scaleY === -1 ? 1 : -1
  }
  arg && isArray(arg)
    ? cropperRef.value?.[eventName]?.(...arg)
    : cropperRef.value?.[eventName]?.(arg)
}

defineExpose({
  cropperRef,
  getCropData,
  getCropBlob,
  getCropFile,
  handleCropper,
})
</script>

<style scoped lang="scss">
.cropper {
  height: 300px;

  &-img {
    width: 400px;
    height: 100%;
    float: left;
    background-color: #ebeef5;

    img {
      display: block;
    }
  }

  &-preview {
    width: 120px;
    margin-left: $space-medium-s;
    float: left;

    h4 {
      font-size: $font-size-medium-x;
      color: var(--el-text-color-secondary);
      font-weight: normal;
      margin-bottom: $space-medium-s;
    }

    &-img {
      width: 120px;
      height: 120px;
      overflow: hidden;
      border: 1px solid var(--el-border-color);
    }
  }
}

:deep(.cropper-view-box),
:deep(.cropper-face) {
  border-radius: v-bind("props.circle ? '50%' : 0");
}
</style>
